package com.gitee.qdbp.jdbc.sql.parse;

import com.gitee.qdbp.jdbc.model.TypedDbVariable;
import com.gitee.qdbp.jdbc.plugins.DbPluginHelper;
import com.gitee.qdbp.jdbc.plugins.SqlDialect;
import com.gitee.qdbp.jdbc.sql.SqlBuffer;
import com.gitee.qdbp.jdbc.sql.SqlBuilder;
import com.gitee.qdbp.staticize.exception.TagException;
import com.gitee.qdbp.staticize.publish.BaseContext;
import com.gitee.qdbp.staticize.utils.OgnlUtils;
import com.gitee.qdbp.tools.utils.StringTools;

/**
 * 环境变量管理类
 *
 * @author zhaohuihua
 * @version 20200912
 * @since 3.2.0
 */
class SqlBufferContext extends BaseContext {

    /** 数据库方言处理接口 **/
    private final SqlDialect dialect;
    /** 插件容器 **/
    private final DbPluginHelper plugins;

    /** 内部构造函数 **/
    protected SqlBufferContext(SqlDialect dialect, DbPluginHelper plugins) {
        super(new SqlCachingWriter(dialect, plugins));
        this.dialect = dialect;
        this.plugins = plugins;
    }

    /** SQL语句 **/
    public SqlBuffer getSqlBuffer() {
        SqlCachingWriter writer = (SqlCachingWriter) this.getWriter();
        return writer.getContent();
    }

    /**
     * 根据表达式从环境变量中获取值<br>
     * ${xxx}, 拼写式参数<br>
     * #{xxx}, 预编译参数<br>
     *
     * @param prefix 表达式前缀类型
     * @param expression 表达式
     * @return 环境变量中的对象
     */
    @Override
    public Object doGetValue(String prefix, String expression) throws TagException {
        if ("#".equals(prefix)) { // 预编译参数
            // #{where.createTime,jdbcType=TIMESTAMP}
            JdbcExpression result = parseJdbcExpression(expression);
            Object value;
            if (result == null) {
                // #{where.createTime}
                value = OgnlUtils.evaluate(expression, true, stack());
            } else {
                // #{where.createTime,jdbcType=TIMESTAMP}
                Object temp = OgnlUtils.evaluate(result.getExpression(), true, stack());
                value = new TypedDbVariable(result.getJdbcType(), temp);
            }
            return new SqlBuffer(dialect, plugins).addVariable(value);
        } else if ("$".equals(prefix)) { // 拼写式参数
            // 如果userName=zhh
            // -- USER_NAME=${userName}     将输出USER_NAME=zhh    (错误)
            // -- USER_NAME=${sql:userName} 将输出USER_NAME='zhh'  (正确)
            // 如果tableName=SYS_USER_INFO
            // -- FROM ${tableName}     将输出FROM SYS_USER_INFO   (正确)
            // -- FROM ${sql:tableName} 将输出FROM 'SYS_USER_INFO' (错误)
            // 再比如日期类型
            // -- ${createTime} 将会输出 createTime.toString()
            // -- 而 ${sql:createTime} 将会根据数据库类型转换为对应的SQL片断
            // ---- 在mysql中将会输出 '2019-06-01 12:34:56.789'
            // ---- 在oracle中将会输出 TO_TIMESTAMP('2019-06-01 12:34:56.789', 'YYYY-MM-DD HH24:MI:SS.FF')
            if (expression.trim().startsWith("sql:")) {
                String exp = StringTools.removePrefix(expression.trim(), "sql:");
                Object value = OgnlUtils.evaluate(exp, true, stack());
                if (value instanceof SqlBuffer) {
                    return ((SqlBuffer) value).getExecutableSqlString();
                } else if (value instanceof SqlBuilder) {
                    return ((SqlBuilder) value).out().getExecutableSqlString();
                } else {
                    return plugins.variableToString(value, dialect);
                }
            } else {
                Object value = OgnlUtils.evaluate(expression, true, stack());
                if (value == null) {
                    return null;
                } else if (value instanceof SqlBuffer) {
                    return ((SqlBuffer) value).getExecutableSqlString();
                } else if (value instanceof SqlBuilder) {
                    return ((SqlBuilder) value).out().getExecutableSqlString();
                } else {
                    return value;
                }
            }
        } else {
            throw new TagException("Expression prefix error.");
        }
    }

    /** 解析带jdbcType的表达式 **/
    // string = where.createTime,jdbcType=TIMESTAMP
    private JdbcExpression parseJdbcExpression(String string) {
        int index = string.indexOf(',');
        if (index <= 0) {
            return null;
        }
        String prefix = string.substring(0, index);
        String suffix = string.substring(index + 1).trim();
        if (suffix.length() == 0) {
            return null;
        }
        String[] array = StringTools.split(suffix, '=');
        if (array.length != 2 || !"jdbcType".equals(array[0])) {
            return null;
        }
        String jdbcType = array[1];
        Integer value = plugins.parseJdbcDataType(jdbcType);
        if (value == null) {
            return null;
        }
        return new JdbcExpression(prefix, value);
    }

    // #{where.createTime,jdbcType=TIMESTAMP}
    private static class JdbcExpression {

        private final int jdbcType;
        private final String expression;

        public JdbcExpression(String expression, int jdbcType) {
            this.expression = expression;
            this.jdbcType = jdbcType;
        }

        public int getJdbcType() {
            return jdbcType;
        }

        public String getExpression() {
            return expression;
        }
    }
}
